package net.gdface.facelog;

import static com.google.common.base.Preconditions.*;
import static com.google.common.base.MoreObjects.firstNonNull;

import java.util.List;
import com.google.common.base.Throwables;

import gu.sql2java.Managers;
import gu.sql2java.TableListener;
import gu.sql2java.exception.RuntimeDaoException;
import net.gdface.facelog.db.Constant;
import net.gdface.facelog.db.FeatureBean;
import net.gdface.facelog.db.PersonBean;
import net.gdface.sdk.fse.BaseFseDbEngine;
import net.gdface.sdk.fse.CodeBean;
import net.gdface.sdk.fse.FeatureSe;
import net.gdface.utils.BinaryUtils;

/**
 * 继承 {@link BaseFseDbEngine}类
 * 将 fl_feature 表中特定SDK版本号的特征记录加入 FSE 引擎,实现内存特征搜索
 * @author guyadong
 *
 */
public class FseEngine extends BaseFseDbEngine<FeatureBean> implements ServiceConstant,Constant {
	private final String sdkVersion;
	private final DaoManagement dm;
	private final boolean asyncInit;
	/** fl_person 字段修改标记 */
	private static ThreadLocal<Integer> modified = new ThreadLocal<Integer>(){
		
		@Override
		protected Integer initialValue() {
			return 0;
		}
		
	};
	
	private int sexGroup(Integer sex){
		sex = firstNonNull(sex,-1);
		switch(sex){
		case 0:
			return FG_FEMALE;
		case 1:
			return FG_MALE;
		default:
			// 未知性别
			return (FG_FEMALE | FG_FEMALE);
		}
	}
	/**
	 * fl_person表侦听器，用于监测fl_person.expiry_date,sex字段是否修改
	 */
	private final TableListener<PersonBean> personTableListener = 
			new TableListener.Adapter<PersonBean>(){
				@Override
				public void beforeUpdate(PersonBean bean) throws RuntimeDaoException {
					modified.set(bean.getModified());
				}

				@Override
				public void afterUpdate(PersonBean bean) throws RuntimeDaoException {
					try {
						int group = 0;
						boolean update = false;
						if((modified.get() & FL_PERSON_ID_SEX_MASK) != 0){
							group |= sexGroup(bean.getSex());
							update =true;
						}
						if(update){
							logger.info(String.format("update features's group 0x%08x for person ID %d", group, bean.getId()));
							List<FeatureBean> featureBeans = dm.daoGetFeatureBeansByPersonIdOnPerson(bean.getId());
							for(FeatureBean featureBean:featureBeans){
								fse.updateGroup(featureIdOf(featureBean), group);
							}
						}
					} catch (RuntimeException e) {
						Throwables.throwIfInstanceOf(e, RuntimeDaoException.class);
						throw new RuntimeDaoException(e);
					}
				}	
			};
	/**
	 * 构造方法
	 * @param fse FSE引擎实例
	 * @param sdkVersion SDK版本号
	 * @param dm 
	 * @param asyncInit 是否异步初始化FSE搜索引擎
	 */
	public FseEngine(FeatureSe fse,String sdkVersion, DaoManagement dm, boolean asyncInit) {
		super(fse);
		this.sdkVersion = checkNotNull(sdkVersion,"sdkVersion is null");
		this.dm = checkNotNull(dm,"dm is null");
		this.asyncInit = asyncInit;
	}

	@Override
	protected boolean beanFilter(FeatureBean bean) {
		return bean == null ? false : sdkVersion.equals(bean.getVersion());
	}

	@Override
	protected int groupOf(FeatureBean bean) {
		int group = 0;
		if(bean != null){
			PersonBean personBean = dm.daoGetPerson(bean.getPersonId());
			if(personBean != null){
				group |= sexGroup(personBean.getSex());
			}
		}
		return group;
	}

	@Override
	protected byte[] featureOf(FeatureBean bean) {
		return null == bean ? null : BinaryUtils.getBytesInBuffer(bean.getFeature());
	}

	@Override
	protected byte[] featureIdOf(FeatureBean bean) {
		return null == bean ? null : BinaryUtils.hex2Bytes(bean.getMd5());
	}

	@Override
	protected String ownerOf(FeatureBean bean) {
		if(null == bean){
			return null;
		}
		return bean.getPersonId() == null ? null : CodeBean.asImgMD5(bean.getPersonId()) ;
	}

	@Override
	public void init() {
		if(asyncInit){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					FseEngine.super.init();
					
				}
			},"fse-init-async").start();
		}else{
			super.init();
		}
		Managers.managerOf(PersonBean.class).registerListener(personTableListener);
	}
}
